package com.scheduling.common.security.jwt;

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.ExpiredJwtException;
import io.jsonwebtoken.JwtException;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.MalformedJwtException;
import io.jsonwebtoken.UnsupportedJwtException;
import io.jsonwebtoken.security.Keys;
import jakarta.annotation.PostConstruct;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import javax.crypto.SecretKey;
import java.nio.charset.StandardCharsets;
import java.util.Date;
import java.util.Iterator;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;

/**
 * jwt服务类
 *
 * @author 李贝依
 * @since 1.0.0
 */
@Service
public class JwtService {

    private static final Logger logger = LoggerFactory.getLogger(JwtService.class);

    // 从配置文件读取密钥和过期时间
    @Value("${jwt.secret:myDefaultSecretKeyForDevelopmentOnlyMustBe32CharsLong}")
    private String secretKey;

    @Value("${jwt.expiration:86400000}") // 默认24小时
    private long jwtExpiration;

    @Value("${jwt.refresh-expiration:604800000}") // 默认7天
    private long refreshExpiration;

    // JWT签名密钥
    private SecretKey key;

    // 黑名单令牌集合 - 线程安全
    private final Set<String> blacklistedTokens = ConcurrentHashMap.newKeySet();

    // 用户活跃令牌映射 - 用于管理每个用户的令牌
    private final Map<String, Set<String>> userTokens = new ConcurrentHashMap<>();

    @PostConstruct
    public void init() {
        // 初始化签名密钥
        if (secretKey.length() < 32) {
            logger.warn("JWT密钥长度过短，建议使用32位以上的密钥");
            // 如果密钥太短，用默认密钥补充
            secretKey = "myDefaultSecretKeyForDevelopmentOnlyMustBe32CharsLong";
        }

        // 使用JJWT推荐的方式创建密钥
        this.key = Keys.hmacShaKeyFor(secretKey.getBytes(StandardCharsets.UTF_8));

        logger.info("JWT服务初始化完成");
    }

    /**
     * 生成访问令牌
     */
    public String generateToken(String userId) {
        return generateToken(userId, jwtExpiration);
    }

    /**
     * 生成刷新令牌
     */
    public String generateRefreshToken(String userId) {
        return generateToken(userId, refreshExpiration);
    }

    /**
     * 生成令牌的通用方法
     */
    private String generateToken(String userId, long expiration) {
        Date now = new Date();
        Date expiryDate = new Date(now.getTime() + expiration);

        String token = Jwts.builder()
                .subject(userId)
                .issuedAt(now)
                .expiration(expiryDate)
                .id(UUID.randomUUID().toString()) // 添加唯一标识
                .signWith(key, Jwts.SIG.HS256) // 使用新的API
                .compact();

        // 记录用户的令牌
        userTokens.computeIfAbsent(userId, k -> ConcurrentHashMap.newKeySet()).add(token);

        logger.debug("为用户 {} 生成了新的JWT令牌", userId);
        return token;
    }

    /**
     * 从令牌中获取用户ID
     */
    public String getUserIdFromToken(String token) {
        try {
            Claims claims = getClaimsFromToken(token);
            return claims != null ? claims.getSubject() : null;
        } catch (Exception e) {
            logger.warn("从令牌中提取用户ID失败: {}", e.getMessage());
            return null;
        }
    }

    /**
     * 从令牌中获取Claims
     */
    public Claims getClaimsFromToken(String token) {
        try {
            return Jwts.parser()
                    .verifyWith(key) // 使用新的API
                    .build()
                    .parseSignedClaims(token)
                    .getPayload();
        } catch (ExpiredJwtException e) {
            logger.debug("JWT令牌已过期: {}", e.getMessage());
            return null;
        } catch (UnsupportedJwtException e) {
            logger.warn("不支持的JWT令牌: {}", e.getMessage());
            return null;
        } catch (MalformedJwtException e) {
            logger.warn("JWT令牌格式错误: {}", e.getMessage());
            return null;
        } catch (JwtException e) {
            logger.warn("JWT签名验证失败: {}", e.getMessage());
            return null;
        } catch (IllegalArgumentException e) {
            logger.warn("JWT令牌参数非法: {}", e.getMessage());
            return null;
        }
    }

    /**
     * 验证令牌是否有效
     */
    public boolean isTokenValid(String token) {
        try {
            // 检查是否在黑名单中
            if (blacklistedTokens.contains(token)) {
                logger.debug("令牌在黑名单中");
                return false;
            }

            // 解析并验证令牌
            Claims claims = getClaimsFromToken(token);
            if (claims == null) {
                return false;
            }

            // 检查是否过期
            Date expiration = claims.getExpiration();
            if (expiration.before(new Date())) {
                logger.debug("令牌已过期");
                return false;
            }

            return true;
        } catch (Exception e) {
            logger.warn("令牌验证失败: {}", e.getMessage());
            return false;
        }
    }

    /**
     * 获取令牌过期时间
     */
    public Date getExpirationDateFromToken(String token) {
        Claims claims = getClaimsFromToken(token);
        return claims != null ? claims.getExpiration() : null;
    }

    /**
     * 检查令牌是否即将过期（1小时内）
     */
    public boolean isTokenExpiringSoon(String token) {
        Date expiration = getExpirationDateFromToken(token);
        if (expiration == null) {
            return true;
        }

        long timeUntilExpiration = expiration.getTime() - System.currentTimeMillis();
        return timeUntilExpiration < 3600000; // 1小时 = 3600000毫秒
    }

    /**
     * 刷新令牌
     */
    public String refreshToken(String token) {
        try {
            Claims claims = getClaimsFromToken(token);
            if (claims == null) {
                return null;
            }

            String userId = claims.getSubject();

            // 将旧令牌加入黑名单
            blacklistToken(token);

            // 生成新令牌
            return generateToken(userId);
        } catch (Exception e) {
            logger.warn("刷新令牌失败: {}", e.getMessage());
            return null;
        }
    }

    /**
     * 将单个令牌加入黑名单
     */
    public void blacklistToken(String token) {
        if (token != null && !token.trim().isEmpty()) {
            blacklistedTokens.add(token);
            logger.debug("令牌已加入黑名单");
        }
    }

    /**
     * 注销用户的所有令牌
     */
    public void invalidateUserTokens(String userId) {
        Set<String> tokens = userTokens.get(userId);
        if (tokens != null) {
            // 将用户的所有令牌加入黑名单
            blacklistedTokens.addAll(tokens);
            // 清除用户令牌记录
            userTokens.remove(userId);
            logger.info("用户 {} 的所有令牌已失效", userId);
        }
    }

    /**
     * 注销特定令牌
     */
    public void invalidateToken(String token) {
        if (token != null) {
            // 获取用户ID
            String userId = getUserIdFromToken(token);

            // 加入黑名单
            blacklistToken(token);

            // 从用户令牌记录中移除
            if (userId != null) {
                Set<String> userTokenSet = userTokens.get(userId);
                if (userTokenSet != null) {
                    userTokenSet.remove(token);
                    if (userTokenSet.isEmpty()) {
                        userTokens.remove(userId);
                    }
                }
            }
        }
    }

    /**
     * 清理过期的黑名单令牌（定期调用以释放内存）
     */
    public void cleanupExpiredTokens() {
        Iterator<String> iterator = blacklistedTokens.iterator();
        int removedCount = 0;

        while (iterator.hasNext()) {
            String token = iterator.next();
            Date expiration = getExpirationDateFromToken(token);

            // 如果令牌已过期，从黑名单中移除
            if (expiration != null && expiration.before(new Date())) {
                iterator.remove();
                removedCount++;
            }
        }

        if (removedCount > 0) {
            logger.info("清理了 {} 个过期的黑名单令牌", removedCount);
        }
    }

    /**
     * 获取黑名单大小
     */
    public int getBlacklistSize() {
        return blacklistedTokens.size();
    }

    /**
     * 获取活跃用户数量
     */
    public int getActiveUsersCount() {
        return userTokens.size();
    }

    /**
     * 验证并获取用户信息
     */
    public Optional<String> validateAndGetUser(String token) {
        if (isTokenValid(token)) {
            String userId = getUserIdFromToken(token);
            return Optional.ofNullable(userId);
        }
        return Optional.empty();
    }
}